package org.mycollections;

import java.util.Iterator;

public final class MyArrayList implements MyList, RandomAccess {

    private final static int DEFAULT_CAPACITY = 10;

    private static final String CAPACITY_LESS_EQUAL_ZERO_EX = "Array buffer.length can't be less then or equal to zero!";
    private static final String ILLEGAL_CAPACITY_EX = "Capacity can't be less then current capacity or size!";
    private static final String WRONG_INDEX_EX = "Wrong index!";

    Object[] buffer;
    private int size;

    public MyArrayList() {
        buffer = createArray(DEFAULT_CAPACITY);
    }

    public MyArrayList(MyList c) {
        buffer = c.toArray();
        size = buffer.length;
    }

    public MyArrayList(int initialCapacity) {
        buffer = createArray(initialCapacity);
    }

    //увеличивает вместимость массива до minCapacity
    public void ensureCapacity(int minCapacity) {

        if (minCapacity < size || buffer.length >= minCapacity) {
            throw new IllegalArgumentException(ILLEGAL_CAPACITY_EX);
        }

        Object[] temp = createArray(minCapacity);
        System.arraycopy(buffer, 0, temp, 0, size);

        buffer = temp;
    }

    //приводит размер массива (buffer.length) к его реально используемому значению
    public void trimToSize() {

        if (size == buffer.length) {
            return;
        }

        buffer = getTrimmed();
    }

    @Override
    public void add(Object e) {

        if (buffer.length == size) {
            ensureCapacity(buffer.length + DEFAULT_CAPACITY);
        }
        buffer[size] = e;
        size++;
    }

    @Override
    public void add(int index, Object e) {

        checkIndex(index);

        if (buffer.length == size) {
            ensureCapacity(buffer.length + DEFAULT_CAPACITY);
        }

        Object prev = buffer[index];
        Object temp;
        for (int i = index + 1; i < size + 1; i++) {

            temp = buffer[i];
            buffer[i] = prev;
            prev = temp;
        }

        buffer[index] = e;
        size++;
    }

    @Override
    public void addAll(Object[] c) {

        int freeSpace = buffer.length - size;
        int lackSpace = c.length - freeSpace;
        if (lackSpace > 0) {
            ensureCapacity(buffer.length + lackSpace + DEFAULT_CAPACITY);
        }

        int i = size;
        for (Object o : c) {
            buffer[i++] = o;
        }
        size += c.length;
    }

    @Override
    public void addAll(int index, Object[] c) {

        checkIndex(index);

        int freeSpace = buffer.length - size;
        int lackSpace = c.length - freeSpace;
        if (lackSpace > 0) {
            ensureCapacity(buffer.length + lackSpace + DEFAULT_CAPACITY);
        }

        int i;
        int toReplaceCount = size - index;
        Object[] temp = new Object[toReplaceCount];
        for (i = 0; i < toReplaceCount; i++) {
            temp[i] = buffer[index + i];
        }
        for (i = 0; i < toReplaceCount; i++) {
            buffer[index + c.length + i] = temp[i];
        }

        i = index;
        for (Object o : c) {
            buffer[i++] = o;
        }
        size += c.length;
    }

    @Override
    public Object get(int index) {

        checkIndex(index);
        return buffer[index];
    }

    @Override
    public Object remove(int index) {

        checkIndex(index);

        Object obj = buffer[index];

        System.arraycopy(buffer, index + 1, buffer, index, size - 1 - index);
        size--;

        return obj;
    }

    @Override
    public void clear() {
        for (int i = 0; i < size; i++) {
            buffer[i] = null;
        }
        size = 0;
    }

    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    @Override
    public void set(int index, Object e) {

        checkIndex(index);
        buffer[index] = e;
    }

    @Override
    public int indexOf(Object o) {

        int i = -1;
        while (buffer[++i] != o && i < size) {
        }
        ;
        if (i == size) {
            i = -1;
        }
        return i;
    }

    @Override
    public int size() {
        return size;
    }

    @Override
    public Object[] toArray() {
        return getTrimmed();
    }

    @Override
    public Iterator iterator() {

        final MyList thisObj = this;

        Iterator it = new Iterator() {

            private int currentIndex = 0;

            @Override
            public boolean hasNext() {
                return currentIndex < size;
            }

            @Override
            public Object next() {
                return buffer[currentIndex++];
            }

            @Override
            public void remove() {
                thisObj.remove(--currentIndex);
                currentIndex--;
            }
        };

        return it;
    }

    private Object[] createArray(int capacity) {

        if (capacity <= 0) {
            throw new IllegalArgumentException(CAPACITY_LESS_EQUAL_ZERO_EX);
        }
        return new Object[capacity];
    }

    private Object[] getTrimmed() {

        Object[] temp = createArray(size);
        System.arraycopy(buffer, 0, temp, 0, size);

        return temp;
    }

    private void checkIndex(int index) {
        if (index > size - 1) {
            throw new IndexOutOfBoundsException(WRONG_INDEX_EX);
        }
    }
}
